<template>
  <jb-page>
    <template #title>Function Call 测试</template>
    <template #opt>
      <jb-btn type="primary" ghost :drawerComponent="FunctionManagement"
              :drawerComponentProps="{selectList:selectList}" :drawerArgs="{width: '1200px'}"
              @drawerYesClick="handleDrawerYesClick">选择Function资源
      </jb-btn>
    </template>
    <template #default="{list,tableStartIndex}">
      <n-layout has-sider style="height: 100%">
        <n-layout class="left-content">
          <div style="height: 100%" class="chat_panel" ref="chatContainer">
            <div v-if="showAISetting"
                 style="display:flex;flex-direction:column;position: absolute;bottom: 140px;right: 80px;width: 360px;background-color:#FFF;border-radius: 16px;box-shadow: 0px 0px 6px 1px rgba(0,0,0,0.1);padding: 18px 16px">
              <div
                  style="width: 100%;display: flex;justify-content: space-between;align-items: center;border-bottom: 1px solid #ccc;padding: 10px">
                <span style="font-size: 18px">聊天设置</span>
                <div style="cursor: pointer" @click="showAISetting=false">
                  <jb-icon icon="iconamoon:close-bold" style="font-size: 20px;color: #333"></jb-icon>
                </div>

              </div>

              <n-form style="margin-top: 24px" ref="formRef" :model="defaultFuncAIConfig"
                      label-placement="top" label-width="120" require-mark-placement="right-hanging">
                <n-form-item label="选择AI模型" path="name">
                  <jb-select url="aiResource/listAllEnabledType" labelField="name" placeholder="请选择AI模型"
                             valueField="sn" v-model:value="defaultFuncAIConfig.model"></jb-select>
                </n-form-item>
                <n-form-item label="MaxToken值" path="sn">
                  <div class="flex-col">
                    <n-input-number round style="width: 100%"
                                    v-model:value="defaultFuncAIConfig.maxTokens" placeholder="请输入MaxToken值"
                                    max="8192">
                      <template #suffix>
                        tokens
                      </template>
                    </n-input-number>
                    <span style="font-size: 12px">范围: 1-8192</span>
                  </div>

                </n-form-item>
                <n-form-item label="Temperature" path="">
                  <n-input-number round style="width: 100%"
                                  v-model:value="defaultFuncAIConfig.temperature" :precision="2"
                                  placeholder="请输入Temperature值">
                  </n-input-number>
                </n-form-item>
                <n-form-item label="Top P" path="">
                  <div style="display: flex;flex-direction: column">
                    <n-slider v-model:value="defaultFuncAIConfig.topP" :step="0.1" :max="1"/>
                    <span
                        style="font-size: 12px">调节句子生成的多样性，值越大生成的内容越丰富多样，但它和Temperature只能设置一个</span>
                  </div>
                </n-form-item>
                <n-form-item label="Top K" path="">
                  <div style="display: flex;flex-direction: column">
                    <n-input-number round style="width: 100%" :precision="0"
                                    v-model:value="defaultFuncAIConfig.topK" placeholder="请输入TopK值" max="100">
                    </n-input-number>
                    <span
                        style="font-size: 12px">限制每次生成时考虑的词汇数量，值越大创造性越强。该选项不是所有大模型都支持。</span>
                  </div>
                </n-form-item>


              </n-form>
            </div>
          </div>
        </n-layout>

        <n-layout-sider bordered collapse-mode="width" :collapsed-width="0" :width="300"
                        content-style="padding: 16px;border-left:1px solid rgb(241 241 241)">
          <div class="selected-items-container">
            <div class="selected-header">
              <span>已选择 {{ selectList.length }} 项</span>
            </div>
            <div class="selected-content">
              <template v-if="selectList.length">
                <div v-for="item in selectList" :key="item.id" class="selected-item">
                  <span class="item-text">{{ item.name || item.briefInfo }}</span>
                  <n-button circle size="tiny" type="error" class="delete-btn"
                            @click="handleRemoveItem(item)">
                    <template #icon>
                      <n-icon>
                        <close/>
                      </n-icon>
                    </template>
                  </n-button>
                </div>
              </template>
              <div v-else class="empty-text">
                暂无选中项
              </div>
            </div>
          </div>
        </n-layout-sider>
      </n-layout>
    </template>
  </jb-page>
</template>

<script setup>
import markdownit from 'markdown-it'
import {
  onActivated, onDeactivated,
  onBeforeUnmount,
  onMounted,
  ref,
  watch
} from "vue";
import {
  JBoltApi
} from "@/service/request/index.js";
import AiChat from "@/components/_builtin/jb-ai-chat/index.js";

import {
  storeToRefs
} from "pinia";
import {
  useWebSocketStore
} from "@/stores/websocket.js";
import {
  Close
} from "@vicons/ionicons5";
import FunctionManagement from "./cpns/param-management/index.vue"
import {useMessage} from "naive-ui";

const webSocketStore = storeToRefs(useWebSocketStore())
const md = markdownit({
  html: true,
  breaks: true,
  linkify: true,
  typographer: true
});
const appId = ref(2)
const tableRef = ref(null)
const chatContainer = ref(null)
const chatInstance = ref(null)
const chatState = ref({
  pageSize: 20,
  hasMore: false,
  startId: null,

})
const showAISetting = ref(false)

//默认当前的ai模型的对象
const defaultFuncAIConfig = ref({
  systemRole: '',
  AiResourceId: null,
  maxTokens: 2000,
  topP: 0,
  topK: 0,
  model: '',
  useContext: false,
  stream: false,
  temperature: 0.5

})
const message = useMessage()
//默认当前的session对象
const currentSession = ref({})
//默认的functioncall的ids配置对象
const defaultFuncIdsConfig = ref({
  functions: []
})
//监听当前的session对象
watch(() => currentSession.value, (newV) => {
  if (newV) {
    if (chatInstance.value) {
      chatInstance.value.destroy();
    }
    if (chatContainer.value) {
      chatState.value = {
        pageSize: 20,
        hasMore: false,
        startId: null,
      }
      loadChatInstance()
    }
  }

})
watch(() => webSocketStore.ws.value, () => {
  console.debug("websock重连")
  if (chatInstance.value) {
    chatInstance.value.updateWebSocket(webSocketStore.ws.value)
  }
})

watch(() => chatContainer.value, (newV) => {
  if (newV && !chatInstance.value) {
    loadChatInstance()
  }
})
//监听Ai模型的配置
watch(() => defaultFuncAIConfig.value, (async (newVal, oldVal) => {
  if (newVal?.id != oldVal?.id) return;
  // console.log("AI配置变动：", JSON.stringify(newVal));
  await JBoltApi.post('/aiAppConfig/updateConfig', {
    linkType: "session",
    linkId: currentSession.value.id,
    configType: "llmConfig",
    configValue: JSON.stringify(newVal)
  });
}), {
  deep: true
})
//监听functioniCall的配置
watch(() => defaultFuncIdsConfig.value, (async (newVal, oldVal) => {
  if (newVal?.id != oldVal?.id) return;
  // console.log("functioncall配置变动：", JSON.stringify(newVal));
  await JBoltApi.post('/aiAppConfig/updateConfig', {
    linkType: "session",
    linkId: currentSession.value.id,
    configType: "funcCallConfig",
    configValue: JSON.stringify(newVal)
  });
}), {
  deep: true
})

const selectList = ref([])

function handleRemoveItem(item) {
  // console.log(item,"移除item")
  // 从选中列表中移除
  selectList.value = selectList.value.filter(i => i.id !== item.id)

  // 取消表格中对应行的选中状态
  const $table = tableRef.value
  if ($table) {
    $table.setCheckboxRow([item], false)
  }

  defaultFuncIdsConfig.value.functions = selectList.value.map(item => item.id)
}

function handleDrawerYesClick({
                                componentRef,
                                close
                              }) {

  componentRef.submit((data) => {
    selectList.value = data;
    defaultFuncIdsConfig.value.functions = data.map(item => item.id);
    close();
  })

}

function loadChatInstance() {
  //初始化aichat
  chatInstance.value = new AiChat(chatContainer.value, {
        // websocketUrl: 'ws://192.168.31.118:8013/ws',
        websocketInstance: webSocketStore.ws.value,
        app: appId.value,
        showAvatar: false,
        sessionId: currentSession.value.id,
        global: null,
        onMsgReceive: (data) => {
        },
        onMsgSend: (msg) => {
        },
        onMsgToDelete: async (id) => {
          console.debug("删除消息", id)
          let {
            result
          } = await JBoltApi.get(`/aiAppMessage/delMsgById/${id}`)
          console.log(result, "----")
          if (result.code == 0) {
            message.success("删除成功");
            return true;
          } else {
            message.error("删除失败");
            return false;
          }
        },
        onSettingClick: (chatInsence) => {
          showAISetting.value = !showAISetting.value
        },
        onMarkdownRender: (markdown) => {
          return md.render(markdown);
        },
        onHistoryLoad: async (startId, global) => {
          // console.log("加载历史记录...", startId, chatState.value.hasMore)
          if (chatState.value.hasMore == false) return;
          let {
            result
          } = await JBoltApi.get('/aiAppMessage/getListByPage', {
            sessionId: currentSession.value.id,
            startId,
            pageSize: chatState.value.pageSize,
          })

          let {
            list,
            totalPage
          } = result.data
          chatState.value.hasMore = chatState.value.pageSize == list?.length;
          // console.log("加载历史记录开始", list)
          if (!list || !list.length) {
            // console.log(currentChat.value.hasMsg)
            return false;
          }
           console.debug("加载历史记录完成...", list)
          chatState.value.hasMsg = true;
          return list.map(item => {
            return {
              id: item.id,
              flag: item.role == "user" ? item.id : item.linkId,
              data: {
                role: item.role,
                format: item.role == 'assistant' ? "markdown" : "",
                content: {
                  text: item.content,
                },
                reference: item.reference,
                errorMsg: item.errorMsg,
              },

              createTime: item.createTime
            }
          })
        }

      },
  )
}


watch(() => useWebSocketStore().ws.value, (newV) => {

  if (chatContainer.value && newV) {
    chatInstance.value.updateWebSocket(useWebSocketStore().ws.value)
  }
})

function addSession() {
  JBoltApi.post('/aiAppSession/add', {
    name: '新的function Call会话',
    appId: appId.value,
    config: {
      "llmConfig": defaultFuncAIConfig.value,
      "funcCallConfig": defaultFuncIdsConfig.value,
    }
  }).then((res) => {
    currentSession.value = res.result.data;
  });
}


onMounted(() => {
  // 添加会话
  addSession();
})
onBeforeUnmount(() => {
  if (chatInstance.value) {
    chatInstance.value.destroy()
  }
})
onActivated(() => {
  if (!chatInstance.value && currentSession.value.id) {
    // console.log("aiChat组件被激活,当前被选中的id:" + currentSession.value.id)
    chatState.value.hasMore = true
    loadChatInstance()
  }
})

onDeactivated(() => {
  if (chatInstance.value) {
    chatInstance.value.destroy()
    chatInstance.value = null;
  }
})
</script>


<style scoped lang="scss">
.left-content {
  display: flex;
  flex-direction: column;
  height: 100%;

}

.left-content:deep(.n-layout-scroll-container) {
  display: flex;
  flex-direction: column;
}

.selected-items-container {
  height: 100%;
  display: flex;
  flex-direction: column;
}

.selected-header {
  padding: 8px 0;
  font-weight: 500;
  border-bottom: 1px solid var(--n-border-color);
  margin-bottom: 12px;
}

.selected-content {
  flex: 1;
  overflow-y: auto;
}

.selected-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 12px;
  margin-bottom: 8px;
  border-radius: 4px;
  background-color: var(--n-card-color);
  transition: all 0.3s;
}

.selected-item:hover {
  background-color: var(--n-table-header-color);
}

.item-text {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin-right: 8px;
}

.delete-btn {
  opacity: 0.8;
}

.delete-btn:hover {
  opacity: 1;
}

.empty-text {
  text-align: center;
  color: var(--n-text-color-disabled);
  padding: 32px 0;
}
</style>